package com.miniotest.utils;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.FFmpegExecutor;
import net.bramp.ffmpeg.FFmpegUtils;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.builder.FFmpegOutputBuilder;
import net.bramp.ffmpeg.job.FFmpegJob;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import net.bramp.ffmpeg.probe.FFmpegStream;
import net.bramp.ffmpeg.progress.Progress;
import net.bramp.ffmpeg.progress.ProgressListener;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Component
public class FFMpegUtils {
    @Resource
    private FFmpeg ffmpeg;
    @Resource
    private FFprobe ffprobe;

    /**
     * 本地文件转m3u8 支持: .mp4 | .flv | .avi | .mov | .wmv | .wav
     * @param resourceFilePath tempFileFolder \\ md5 \\ fileName
     * @param targetFileName
     * @return
     */
    public String localFileToM3u8(String resourceFilePath,String targetFileName){
        try {
            log.info("解析文件，转m3u8");

            final FFmpegProbeResult probe = ffprobe.probe(resourceFilePath);
            final List<FFmpegStream> streams = probe.getStreams().stream().filter(fFmpegStream -> fFmpegStream.codec_type != null).collect(Collectors.toList());
            final Optional<FFmpegStream> audioStream = streams.stream().filter(fFmpegStream -> FFmpegStream.CodecType.AUDIO.equals(fFmpegStream.codec_type)).findFirst();
            final Optional<FFmpegStream> videoStream = streams.stream().filter(fFmpegStream -> FFmpegStream.CodecType.VIDEO.equals(fFmpegStream.codec_type)).findFirst();
            if (!audioStream.isPresent()) {
                log.error("未发现音频流");
            }
            if (!videoStream.isPresent()) {
                log.error("未发现视频流");
            }

            // m3u8文件存储路径,需要显示声明以".m3u8"作为文件后缀
            // (1)m3u8文件所在目录，需要存储转化后的.m3u8文件及其.ts文件  ——tempFileFolder \\ md5 \\ m3u8
            String targetFileFolderPath = resourceFilePath.substring(0,resourceFilePath.lastIndexOf("\\")+1).concat("m3u8");
            File targetFileFolder = new File(targetFileFolderPath);
            if(!targetFileFolder.exists()){
                targetFileFolder.mkdirs();
            }
            // (2)m3u8文件路径 ————tempFileFolder \\ md5 \\ m3u8 \\ fileName.m3u8
            String targetFilePath = targetFileFolderPath.concat("\\").concat(targetFileName).concat(".m3u8");
            // 转码
            FFmpegOutputBuilder builder = new FFmpegBuilder()
                    .setInput(resourceFilePath)
                    .overrideOutputFiles(true)
                    .addOutput(targetFilePath)
                    .setFormat(probe.getFormat().format_name)
                    .setStrict(FFmpegBuilder.Strict.STRICT)
                    .setFormat("hls")
                    .setPreset("ultrafast")
                    .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_time", "8", "-hls_list_size", "0", "-threads", "12");
//                .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12")

            if (audioStream.isPresent()) {
                builder = builder
                        .setAudioChannels(1)
                        .setAudioCodec("aac")
                        //.setAudioBitRate(audioStream.get().bit_rate)
                        .setAudioBitRate(32768)
                        .setAudioSampleRate(audioStream.get().sample_rate);
            }

            FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
            // Run a one-pass encode
            FFmpegJob job = executor.createJob(builder.done(), new ProgressListener() {
                // Using the FFmpegProbeResult determine the duration of the input
                final double duration_ns = probe.getFormat().duration * TimeUnit.SECONDS.toNanos(1);

                @Override
                public void progress(Progress progress) {
                    double percentage = progress.out_time_ns / duration_ns;

                    // Print out interesting information about the progress
                    log.info("### localFileToM3u8 progress, {}", String.format(
                            "[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx",
                            percentage * 100,
                            progress.status,
                            progress.frame,
                            FFmpegUtils.toTimecode(progress.out_time_ns, TimeUnit.NANOSECONDS),
                            progress.fps.doubleValue(),
                            progress.speed
                    ));
                }
            });
            job.run();
            log.info("文件转m3u8成功");
            // 删除源文件
            File resourceFile = new File(resourceFilePath);
            if(resourceFile.isFile() && resourceFile.exists()){
                resourceFile.delete();
                System.gc();
                log.info("合并文件已删除");
            }
            // 返回.m3u8文件所在路径
            return targetFilePath;
        } catch (Exception e) {
            log.error("本地文件转m3u8失败",e);
            return null;
        }
    }
}
